#include "LinkedList.h"

BASE(LinkedList, List, 100)

//HIDDEN
node *newNode(void *ptr)
{
	node *n = (node *) malloc(sizeof(node));
	n->ptr = ptr;
	n->next = n->last = 0;

	return n;
}

void incIndexes(node *n)
{
	while(n)
	{
		n->index++;
		n=n->next;
	}
}

void decIndexes(node *n)
{
	while(n)
	{
		n->index--;
		n=n->next;
	}
}

//SHARED
IMPL(void, destroy)
{
	node *n = THIS->start;
	while(n)
	{
		node *p = n;
		D(p->ptr);

		n = n->next;
		free(p);
	}
}

IMPL(unsigned long, size)
{
	if(THIS->end) return THIS->end->index + 1;
	return 0;
}

IMPL(int, isEmpty)
{
	return THIS->end==0;
}

IMPL(int, add, void *ptr)
{
	node *n = newNode(ptr);
	if(!n) return 0;

	if(!THIS->end)
	{
		THIS->end = THIS->start = n;
		n->index=0;
	}
	else
	{
		n->index = THIS->end->index + 1;
		THIS->end->next = n;
		n->last = THIS->end;
		THIS->end = n;
	}

	R(ptr);
	return 1;
}

IMPL(int, insertAt, unsigned long int pos, void *ptr)
{
	node *n = newNode(ptr);
	if(!n) return 0;

	n->index = pos;

	if(THIS->end==0)
	{
		THIS->start = THIS->end = n;
	}
	else if(pos <= THIS->start->index)
	{
		n->next = THIS->start;
		THIS->start->last = n;
		THIS->start = n;
		incIndexes(n->next);
	}
	else if(pos > THIS->end->index)
	{
		n->last = THIS->end;
		THIS->end->next = n;
		THIS->end = n;
	}
	else
	{
		node *p = THIS->start;
		while(p->index<pos) p=p->next;

		incIndexes(p);
		n->next=p;
		n->last=p->last;
		n->last->next=n;
		p->last=n;
	}

	R(ptr);
	return 1;
}

IMPL(int, set, unsigned long int pos, void *ptr)
{
	if(pos>=C(size)()) return 0;

	node *p = THIS->start;
	while(p) {
		if(p->index==pos) {
			D(p->ptr);

			p->ptr = ptr;
			break;
		}
		if(p->index > pos) {
			node *n = newNode(ptr);
			if(!n) return 0;

			n->index = pos;
			n->next = p;
			n->last = p->last;
			p->last = n;
			if(n->last) n->last->next=n;
			break;
		}
		p = p->next;
	}

	R(ptr);
	return 1;
}

IMPL(void *, get, unsigned long int pos)
{
	if(pos>=C(size)()) return INVALID_OBJ;

	node *n = THIS->start;
	while(n) {
		if(n->index==pos) return n->ptr;
		if(n->index>pos) return 0;
		n = n->next;
	}

	return 0;
}

IMPL(long int, indexOf, void *ptr)
{
	node *n = THIS->start;

	while(n) {
		if(n->ptr==ptr) return n->index;
		n = n->next;
	}

	return -1;
}

IMPL(long int, lastIndexOf, void *ptr)
{
	node *n = THIS->end;

	while(n) {
		if(n->ptr==ptr) return n->index;
		n = n->last;
	}

	return -1;
}

IMPL(int, contains, void *ptr)
{
	node *n = THIS->start;

	while(n) {
		if(n->ptr==ptr) return n->index;
		n = n->next;
	}

	return 0;
}

IMPL(int, del, unsigned long int pos)
{
	if(pos>=C(size)()) return 0;

	int result = 0;
	node *n = THIS->start;
	while(n)
	{
		if(n->index==pos)
		{
			result = 1;

			if(n==THIS->start) THIS->start = n->next;
			if(n==THIS->end) THIS->end = n->last;

			if(n->last) n->last->next = n->next;
			if(n->next) n->next->last = n->last;

			decIndexes(n->next);
			D(n->ptr);

			free(n);
			break;
		}
		if(n->index > pos)
		{
			result = 1;

			decIndexes(n);
			break;
		}

		n = n->next;
	}

	return result;
}

IMPL(int, delObj, void *ptr)
{
	node *n = THIS->start;
	while(n)
	{
		if(n->ptr==ptr)
		{
			if(n==THIS->start) THIS->start = n->next;
			if(n==THIS->end) THIS->end = n->last;

			if(n->last) n->last->next = n->next;
			if(n->next) n->next->last = n->last;

			decIndexes(n->next);
			D(n->ptr);

			free(n);
			return 1;
		}

		n = n->next;
	}

	return 0;
}

SETUP_IF(ENTRY(destroy), 
ENTRY(size), ENTRY(isEmpty), 
ENTRY(add), ENTRY(insertAt), 
ENTRY(set), ENTRY(get), 
ENTRY(indexOf), ENTRY(lastIndexOf), 
ENTRY(contains), 
ENTRY(del), ENTRY(delObj) )
